/*
 * FreeRTOS Kernel V10.5.1
 * Copyright (C) 2021 Amazon.com, Inc. or its affiliates.  All Rights Reserved.
 *
 * SPDX-License-Identifier: MIT
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy of
 * this software and associated documentation files (the "Software"), to deal in
 * the Software without restriction, including without limitation the rights to
 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
 * the Software, and to permit persons to whom the Software is furnished to do so,
 * subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
 * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
 * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 *
 * https://www.FreeRTOS.org
 * https://github.com/FreeRTOS
 *
 */

/* Standard includes. */
#include <stdlib.h>

/* Defining MPU_WRAPPERS_INCLUDED_FROM_API_FILE prevents task.h from redefining
 * all the API functions to use the MPU wrappers.  That should only be done when
 * task.h is included from an application file. */
#define MPU_WRAPPERS_INCLUDED_FROM_API_FILE

/* FreeRTOS includes. */
#include "FreeRTOS.h"
#include "task.h"
#include "timers.h"
#include "event_groups.h"

/* Lint e961, e750 and e9021 are suppressed as a MISRA exception justified
 * because the MPU ports require MPU_WRAPPERS_INCLUDED_FROM_API_FILE to be defined
 * for the header files above, but not in this file, in order to generate the
 * correct privileged Vs unprivileged linkage and placement. */
#undef MPU_WRAPPERS_INCLUDED_FROM_API_FILE /*lint !e961 !e750 !e9021 See comment above. */

/* The following bit fields convey control information in a task's event list
 * item value.  It is important they don't clash with the
 * taskEVENT_LIST_ITEM_VALUE_IN_USE definition. */
#if configUSE_16_BIT_TICKS == 1
    #define eventCLEAR_EVENTS_ON_EXIT_BIT    0x0100U
    #define eventUNBLOCKED_DUE_TO_BIT_SET    0x0200U
    #define eventWAIT_FOR_ALL_BITS           0x0400U
    #define eventEVENT_BITS_CONTROL_BYTES    0xff00U            /* 只有8位用于事件组 */
#else
    #define eventCLEAR_EVENTS_ON_EXIT_BIT    0x01000000UL
    #define eventUNBLOCKED_DUE_TO_BIT_SET    0x02000000UL
    #define eventWAIT_FOR_ALL_BITS           0x04000000UL
    #define eventEVENT_BITS_CONTROL_BYTES    0xff000000UL       /* 只有24位用于事件组 */
#endif

typedef struct EventGroupDef_t
{
    EventBits_t uxEventBits;            /* 事件标志位 */
    List_t xTasksWaitingForBits;        /* 等待事件发生的任务列表 */

#if ( configUSE_TRACE_FACILITY == 1 )
    UBaseType_t uxEventGroupNumber;
#endif
} EventGroup_t;

/*
 * Test the bits set in uxCurrentEventBits to see if the wait condition is met.
 * The wait condition is defined by xWaitForAllBits.  If xWaitForAllBits is
 * pdTRUE then the wait condition is met if all the bits set in uxBitsToWaitFor
 * are also set in uxCurrentEventBits.  If xWaitForAllBits is pdFALSE then the
 * wait condition is met if any of the bits set in uxBitsToWait for are also set
 * in uxCurrentEventBits.
 */
static BaseType_t prvTestWaitCondition(const EventBits_t uxCurrentEventBits,
                                        const EventBits_t uxBitsToWaitFor,
                                        const BaseType_t xWaitForAllBits ) PRIVILEGED_FUNCTION;

/**
 * 创建事件标志组
 */
EventGroupHandle_t xEventGroupCreate(void)
{
    EventGroup_t * pxEventBits;
    /* Allocate the event group.  Justification for MISRA deviation as
     * follows:  pvPortMalloc() always ensures returned memory blocks are
     * aligned per the requirements of the MCU stack.  In this case
     * pvPortMalloc() must return a pointer that is guaranteed to meet the
     * alignment requirements of the EventGroup_t structure - which (if you
     * follow it through) is the alignment requirements of the TickType_t type
     * (EventBits_t being of TickType_t itself).  Therefore, whenever the
     * stack alignment requirements are greater than or equal to the
     * TickType_t alignment requirements the cast is safe.  In other cases,
     * where the natural word size of the architecture is less than
     * sizeof( TickType_t ), the TickType_t variables will be accessed in two
     * or more reads operations, and the alignment requirements is only that
     * of each individual read. */
    pxEventBits = (EventGroup_t *) pvPortMalloc(sizeof(EventGroup_t));
    if (pxEventBits != NULL)
    {
        /* 事件位全部清零 */
        pxEventBits->uxEventBits = 0;
        vListInitialise(&(pxEventBits->xTasksWaitingForBits));
        traceEVENT_GROUP_CREATE(pxEventBits);
    }
    else
    {
        traceEVENT_GROUP_CREATE_FAILED();
    }
    return pxEventBits;
}


EventBits_t xEventGroupSync(EventGroupHandle_t xEventGroup,
                             const EventBits_t uxBitsToSet,
                             const EventBits_t uxBitsToWaitFor,
                             TickType_t xTicksToWait)
{
    EventBits_t uxOriginalBitValue, uxReturn;
    EventGroup_t * pxEventBits = xEventGroup;
    BaseType_t xAlreadyYielded;
    BaseType_t xTimeoutOccurred = pdFALSE;

    configASSERT((uxBitsToWaitFor & eventEVENT_BITS_CONTROL_BYTES) == 0);
    configASSERT(uxBitsToWaitFor != 0 );
    #if ((INCLUDE_xTaskGetSchedulerState == 1) || (configUSE_TIMERS == 1))
    {
        configASSERT(!((xTaskGetSchedulerState() == taskSCHEDULER_SUSPENDED) && (xTicksToWait != 0)));
    }
    #endif

    vTaskSuspendAll();
    {
        uxOriginalBitValue = pxEventBits->uxEventBits;

        (void) xEventGroupSetBits(xEventGroup, uxBitsToSet);

        if (((uxOriginalBitValue | uxBitsToSet) & uxBitsToWaitFor) == uxBitsToWaitFor)
        {
            /* All the rendezvous bits are now set - no need to block. */
            uxReturn = ( uxOriginalBitValue | uxBitsToSet );

            /* Rendezvous always clear the bits.  They will have been cleared
             * already unless this is the only task in the rendezvous. */
            pxEventBits->uxEventBits &= ~uxBitsToWaitFor;

            xTicksToWait = 0;
        }
        else
        {
            if ( xTicksToWait != ( TickType_t ) 0 )
            {
                traceEVENT_GROUP_SYNC_BLOCK( xEventGroup, uxBitsToSet, uxBitsToWaitFor );

                /* Store the bits that the calling task is waiting for in the
                 * task's event list item so the kernel knows when a match is
                 * found.  Then enter the blocked state. */
                vTaskPlaceOnUnorderedEventList(&(pxEventBits->xTasksWaitingForBits), (uxBitsToWaitFor | eventCLEAR_EVENTS_ON_EXIT_BIT | eventWAIT_FOR_ALL_BITS ), xTicksToWait);

                /* This assignment is obsolete as uxReturn will get set after
                 * the task unblocks, but some compilers mistakenly generate a
                 * warning about uxReturn being returned without being set if the
                 * assignment is omitted. */
                uxReturn = 0;
            }
            else
            {
                /* The rendezvous bits were not set, but no block time was
                 * specified - just return the current event bit value. */
                uxReturn = pxEventBits->uxEventBits;
                xTimeoutOccurred = pdTRUE;
            }
        }
    }
    xAlreadyYielded = xTaskResumeAll();

    if (xTicksToWait != (TickType_t)0)
    {
        if (xAlreadyYielded == pdFALSE)
        {
            portYIELD_WITHIN_API();
        }

        /* The task blocked to wait for its required bits to be set - at this
         * point either the required bits were set or the block time expired.  If
         * the required bits were set they will have been stored in the task's
         * event list item, and they should now be retrieved then cleared. */
        uxReturn = uxTaskResetEventItemValue();

        if ((uxReturn & eventUNBLOCKED_DUE_TO_BIT_SET ) == (EventBits_t )0)
        {
            /* The task timed out, just return the current event bit value. */
            taskENTER_CRITICAL();
            {
                uxReturn = pxEventBits->uxEventBits;

                /* Although the task got here because it timed out before the
                 * bits it was waiting for were set, it is possible that since it
                 * unblocked another task has set the bits.  If this is the case
                 * then it needs to clear the bits before exiting. */
                if ( ( uxReturn & uxBitsToWaitFor ) == uxBitsToWaitFor )
                {
                    pxEventBits->uxEventBits &= ~uxBitsToWaitFor;
                }
            }
            taskEXIT_CRITICAL();

            xTimeoutOccurred = pdTRUE;
        }
        else
        {
            /* The task unblocked because the bits were set. */
        }

        /* Control bits might be set as the task had blocked should not be
         * returned. */
        uxReturn &= ~eventEVENT_BITS_CONTROL_BYTES;
    }

    traceEVENT_GROUP_SYNC_END(xEventGroup, uxBitsToSet, uxBitsToWaitFor, xTimeoutOccurred);

    /* Prevent compiler warnings when trace macros are not used. */
    ( void ) xTimeoutOccurred;

    return uxReturn;
}

/**
 * 等待指定的事件位
 * @uxBitsToWaitFor: 任务等待的事件位
 * @xTicksToWait: 任务等待的节拍数
 * @xWaitForAllBits : 是否等待uxBitsToWaitFor中的所有事件发生才返回
 */
EventBits_t xEventGroupWaitBits( EventGroupHandle_t xEventGroup,
                                 const EventBits_t uxBitsToWaitFor,
                                 const BaseType_t xClearOnExit,
                                 const BaseType_t xWaitForAllBits,
                                 TickType_t xTicksToWait)
{
    EventGroup_t * pxEventBits = xEventGroup;
    EventBits_t uxReturn, uxControlBits = 0;
    BaseType_t xWaitConditionMet, xAlreadyYielded;
    BaseType_t xTimeoutOccurred = pdFALSE;

    /* Check the user is not attempting to wait on the bits used by the kernel
     * itself, and that at least one bit is being requested. */
    configASSERT(xEventGroup);
    configASSERT((uxBitsToWaitFor & eventEVENT_BITS_CONTROL_BYTES) == 0);
    configASSERT(uxBitsToWaitFor != 0);
    #if ((INCLUDE_xTaskGetSchedulerState == 1) || (configUSE_TIMERS == 1))
    {
        configASSERT(!((xTaskGetSchedulerState() == taskSCHEDULER_SUSPENDED) && (xTicksToWait != 0)));
    }
    #endif

    vTaskSuspendAll();
    {
        const EventBits_t uxCurrentEventBits = pxEventBits->uxEventBits;

        /* Check to see if the wait condition is already met or not. */
        xWaitConditionMet = prvTestWaitCondition(uxCurrentEventBits, uxBitsToWaitFor, xWaitForAllBits);

        /* 等待的事件发生 */
        if (xWaitConditionMet != pdFALSE)
        {
            /* The wait condition has already been met so there is no need to
             * block.
             */
            uxReturn = uxCurrentEventBits;
            xTicksToWait = (TickType_t)0;

            /* Clear the wait bits if requested to do so. */
            if (xClearOnExit != pdFALSE)
            {
                /* 事件发生时，清空等待的事件 */
                pxEventBits->uxEventBits &= ~uxBitsToWaitFor;
            }
        }
        else if (xTicksToWait == (TickType_t)0)
        {
            /* The wait condition has not been met, but no block time was
             * specified, so just return the current value.
             * 事件未发生，但是不进行等待
             */
            uxReturn = uxCurrentEventBits;
            xTimeoutOccurred = pdTRUE;
        }
        else
        {
            /* The task is going to block to wait for its required bits to be
             * set.  uxControlBits are used to remember the specified behaviour of
             * this call to xEventGroupWaitBits() - for use when the event bits
             * unblock the task. */
            if (xClearOnExit != pdFALSE)
            {
                uxControlBits |= eventCLEAR_EVENTS_ON_EXIT_BIT;
            }

            /* 等待所有的事件发生 */
            if (xWaitForAllBits != pdFALSE)
            {
                uxControlBits |= eventWAIT_FOR_ALL_BITS;
            }

            /* Store the bits that the calling task is waiting for in the
             * task's event list item so the kernel knows when a match is
             * found.  Then enter the blocked state.
             * 任务等待的事件，没有发生，加入阻塞队列
             */
            vTaskPlaceOnUnorderedEventList(&(pxEventBits->xTasksWaitingForBits), (uxBitsToWaitFor|uxControlBits), xTicksToWait);

            /* This is obsolete as it will get set after the task unblocks, but
             * some compilers mistakenly generate a warning about the variable
             * being returned without being set if it is not done. */
            uxReturn = 0;

            traceEVENT_GROUP_WAIT_BITS_BLOCK( xEventGroup, uxBitsToWaitFor );
        }
    }
    xAlreadyYielded = xTaskResumeAll();

    /* 任务阻塞等待 */
    if (xTicksToWait != (TickType_t)0)
    {
        /* 任务进入阻塞等待，触发任务调度 */
        if (xAlreadyYielded == pdFALSE)
        {
            portYIELD_WITHIN_API();
        }

        /* The task blocked to wait for its required bits to be set - at this
         * point either the required bits were set or the block time expired.  If
         * the required bits were set they will have been stored in the task's
         * event list item, and they should now be retrieved then cleared.
         * 被阻塞的任务，恢复执行
         */
        uxReturn = uxTaskResetEventItemValue();

        /* 恢复执行，但是等待的事件没发生(等待超时) */
        if ((uxReturn & eventUNBLOCKED_DUE_TO_BIT_SET) == (EventBits_t)0)
        {
            taskENTER_CRITICAL();
            {
                /* The task timed out, just return the current event bit value. */
                uxReturn = pxEventBits->uxEventBits;

                /* It is possible that the event bits were updated between this
                 * task leaving the Blocked state and running again. */
                if (prvTestWaitCondition(uxReturn, uxBitsToWaitFor, xWaitForAllBits) != pdFALSE)
                {
                    if (xClearOnExit != pdFALSE )
                    {
                        pxEventBits->uxEventBits &= ~uxBitsToWaitFor;
                    }
                }

                xTimeoutOccurred = pdTRUE;
            }
            taskEXIT_CRITICAL();
        }
        else
        {
            /* The task unblocked because the bits were set. */
        }

        /* The task blocked so control bits may have been set. */
        uxReturn &= ~eventEVENT_BITS_CONTROL_BYTES;
    }

    traceEVENT_GROUP_WAIT_BITS_END(xEventGroup, uxBitsToWaitFor, xTimeoutOccurred);

    /* Prevent compiler warnings when trace macros are not used. */
    (void)xTimeoutOccurred;

    return uxReturn;
}

/**
 * 将指定的事件位清零
 * @uxBitsToClear: 需要清零的事件位
*/
EventBits_t xEventGroupClearBits(EventGroupHandle_t xEventGroup,
                                  const EventBits_t uxBitsToClear)
{
    EventGroup_t * pxEventBits = xEventGroup;
    EventBits_t uxReturn;

    /* Check the user is not attempting to clear the bits used by the kernel
     * itself. */
    configASSERT(xEventGroup);
    configASSERT((uxBitsToClear & eventEVENT_BITS_CONTROL_BYTES) == 0);

    taskENTER_CRITICAL();
    {
        traceEVENT_GROUP_CLEAR_BITS(xEventGroup, uxBitsToClear);

        /**
         * The value returned is the event group value prior to the bits being
         * cleared.
         * 获取事件组标记位
         */
        uxReturn = pxEventBits->uxEventBits;

        /**
         * Clear the bits
         * 清理掉标记位
         */
        pxEventBits->uxEventBits &= ~uxBitsToClear;
    }
    taskEXIT_CRITICAL();

    /* 返回旧的事件位 */
    return uxReturn;
}

#if ((configUSE_TRACE_FACILITY == 1) && (INCLUDE_xTimerPendFunctionCall == 1) && (configUSE_TIMERS == 1))

BaseType_t xEventGroupClearBitsFromISR(EventGroupHandle_t xEventGroup,
                                        const EventBits_t uxBitsToClear)
{
    BaseType_t xReturn;
    traceEVENT_GROUP_CLEAR_BITS_FROM_ISR( xEventGroup, uxBitsToClear);
    xReturn = xTimerPendFunctionCallFromISR(vEventGroupClearBitsCallback, (void *)xEventGroup, (uint32_t)uxBitsToClear, NULL);

    return xReturn;
}

#endif

EventBits_t xEventGroupGetBitsFromISR(EventGroupHandle_t xEventGroup)
{
    UBaseType_t uxSavedInterruptStatus;
    EventGroup_t const * const pxEventBits = xEventGroup;
    EventBits_t uxReturn;

    uxSavedInterruptStatus = portSET_INTERRUPT_MASK_FROM_ISR();
    {
        uxReturn = pxEventBits->uxEventBits;
    }
    portCLEAR_INTERRUPT_MASK_FROM_ISR(uxSavedInterruptStatus);

    return uxReturn;
}

/**
 * 将指定的事件位设置为1
 * @uxBitsToSet: 待设置的bit位置
*/
EventBits_t xEventGroupSetBits(EventGroupHandle_t xEventGroup,
                                const EventBits_t uxBitsToSet)
{
    ListItem_t * pxListItem;
    ListItem_t * pxNext;
    ListItem_t const * pxListEnd;
    List_t const * pxList;
    EventBits_t uxBitsToClear = 0, uxBitsWaitedFor, uxControlBits;
    EventGroup_t * pxEventBits = xEventGroup;
    BaseType_t xMatchFound = pdFALSE;

    /* Check the user is not attempting to set the bits used by the kernel
     * itself. */
    configASSERT(xEventGroup);
    configASSERT((uxBitsToSet & eventEVENT_BITS_CONTROL_BYTES) == 0);

    pxList = &(pxEventBits->xTasksWaitingForBits);
    pxListEnd = listGET_END_MARKER(pxList);
    vTaskSuspendAll();
    {
        traceEVENT_GROUP_SET_BITS(xEventGroup, uxBitsToSet);

        pxListItem = listGET_HEAD_ENTRY(pxList);

        /**
         * Set the bits.
         * 设置事件标记位
         */
        pxEventBits->uxEventBits |= uxBitsToSet;

        /**
         * See if the new bit value should unblock any tasks.
         * 有任务在等待事件位被设置
         */
        while(pxListItem != pxListEnd)
        {
            pxNext = listGET_NEXT(pxListItem);
            uxBitsWaitedFor = listGET_LIST_ITEM_VALUE(pxListItem);
            xMatchFound = pdFALSE;

            /* Split the bits waited for from the control bits. */
            uxControlBits = uxBitsWaitedFor & eventEVENT_BITS_CONTROL_BYTES;
            /* 计算出任务等待的事件组 */
            uxBitsWaitedFor &= ~eventEVENT_BITS_CONTROL_BYTES;

            if ((uxControlBits & eventWAIT_FOR_ALL_BITS) == (EventBits_t)0)
            {
                /* Just looking for single bit being set. */
                if ((uxBitsWaitedFor & pxEventBits->uxEventBits) != (EventBits_t)0)
                {
                    xMatchFound = pdTRUE;
                }
            }
            else if ((uxBitsWaitedFor & pxEventBits->uxEventBits) == uxBitsWaitedFor)
            {
                /**
                 * All bits are set.
                 */
                xMatchFound = pdTRUE;
            }
            else
            {
                /* Need all bits to be set, but not all the bits were set. */
            }
            /* 等待的事件发生 */
            if (xMatchFound != pdFALSE)
            {
                /* The bits match.  Should the bits be cleared on exit? */
                if ((uxControlBits & eventCLEAR_EVENTS_ON_EXIT_BIT) != (EventBits_t)0)
                {
                    /* 事件发生后，需要被清空 */
                    uxBitsToClear |= uxBitsWaitedFor;
                }

                /* Store the actual event flag value in the task's event list
                 * item before removing the task from the event list.  The
                 * eventUNBLOCKED_DUE_TO_BIT_SET bit is set so the task knows
                 * that is was unblocked due to its required bits matching, rather
                 * than because it timed out.
                 * 任务从等待队列中删除
                 */
                vTaskRemoveFromUnorderedEventList(pxListItem, pxEventBits->uxEventBits|eventUNBLOCKED_DUE_TO_BIT_SET);
            }

            /* Move onto the next list item.  Note pxListItem->pxNext is not
             * used here as the list item may have been removed from the event list
             * and inserted into the ready/pending reading list.
             */
            pxListItem = pxNext;
        }

        /**
         * Clear any bits that matched when the eventCLEAR_EVENTS_ON_EXIT_BIT
         * bit was set in the control word.
         */
        pxEventBits->uxEventBits &= ~uxBitsToClear;
    }
    (void) xTaskResumeAll();

    /* 返回剩余的事件位 */
    return pxEventBits->uxEventBits;
}

void vEventGroupDelete(EventGroupHandle_t xEventGroup)
{
    EventGroup_t * pxEventBits = xEventGroup;
    const List_t * pxTasksWaitingForBits;

    configASSERT(pxEventBits);

    pxTasksWaitingForBits = &(pxEventBits->xTasksWaitingForBits);

    vTaskSuspendAll();
    {
        traceEVENT_GROUP_DELETE(xEventGroup);

        while(listCURRENT_LIST_LENGTH(pxTasksWaitingForBits) > (UBaseType_t)0)
        {
            /* Unblock the task, returning 0 as the event list is being deleted
             * and cannot therefore have any bits set. */
            configASSERT(pxTasksWaitingForBits->xListEnd.pxNext != (const ListItem_t *) &(pxTasksWaitingForBits->xListEnd));
            vTaskRemoveFromUnorderedEventList(pxTasksWaitingForBits->xListEnd.pxNext, eventUNBLOCKED_DUE_TO_BIT_SET);
        }
    }
    (void) xTaskResumeAll();


    /* The event group can only have been allocated dynamically - free
     * it again. */
    vPortFree(pxEventBits);

}

/* For internal use only - execute a 'set bits' command that was pended from
 * an interrupt.
 */
void vEventGroupSetBitsCallback(void * pvEventGroup,
                                 const uint32_t ulBitsToSet)
{
    (void)xEventGroupSetBits(pvEventGroup, (EventBits_t) ulBitsToSet);
}


/* For internal use only - execute a 'clear bits' command that was pended from
 * an interrupt.
 */
void vEventGroupClearBitsCallback(void * pvEventGroup,
                                   const uint32_t ulBitsToClear)
{
    (void)xEventGroupClearBits(pvEventGroup, (EventBits_t)ulBitsToClear);
}


/**
 * 是否有等待的事件发生
 */
static BaseType_t prvTestWaitCondition(const EventBits_t uxCurrentEventBits,
                                        const EventBits_t uxBitsToWaitFor,
                                        const BaseType_t xWaitForAllBits)
{
    BaseType_t xWaitConditionMet = pdFALSE;

    /* 等待任意一个事件发生即可 */
    if (xWaitForAllBits == pdFALSE)
    {
        /* Task only has to wait for one bit within uxBitsToWaitFor to be
         * set.  Is one already set? */
        if ((uxCurrentEventBits & uxBitsToWaitFor) != (EventBits_t)0)
        {
            xWaitConditionMet = pdTRUE;
        }
    }
    else
    {
        /* Task has to wait for all the bits in uxBitsToWaitFor to be set.
         * Are they set already?
         * 任务等待所有的事件都发生
         */
        if ((uxCurrentEventBits & uxBitsToWaitFor) == uxBitsToWaitFor)
        {
            xWaitConditionMet = pdTRUE;
        }
    }

    return xWaitConditionMet;
}

#if ((configUSE_TRACE_FACILITY == 1) && (INCLUDE_xTimerPendFunctionCall == 1) && (configUSE_TIMERS == 1))

    BaseType_t xEventGroupSetBitsFromISR(EventGroupHandle_t xEventGroup,
                                          const EventBits_t uxBitsToSet,
                                          BaseType_t * pxHigherPriorityTaskWoken)
    {
        BaseType_t xReturn;

        traceEVENT_GROUP_SET_BITS_FROM_ISR(xEventGroup, uxBitsToSet);
        xReturn = xTimerPendFunctionCallFromISR(vEventGroupSetBitsCallback, (void *)xEventGroup, (uint32_t) uxBitsToSet, pxHigherPriorityTaskWoken);

        return xReturn;
    }

#endif

#if (configUSE_TRACE_FACILITY == 1)

    UBaseType_t uxEventGroupGetNumber(void * xEventGroup)
    {
        UBaseType_t xReturn;
        EventGroup_t const * pxEventBits = (EventGroup_t *)xEventGroup;
        if (xEventGroup == NULL)
        {
            xReturn = 0;
        }
        else
        {
            xReturn = pxEventBits->uxEventGroupNumber;
        }

        return xReturn;
    }

#endif

#if (configUSE_TRACE_FACILITY == 1)

void vEventGroupSetNumber(void * xEventGroup,
                           UBaseType_t uxEventGroupNumber)
{
    ((EventGroup_t *) xEventGroup)->uxEventGroupNumber = uxEventGroupNumber;
}

#endif

